对象属性枚举顺序

ES6规定了对象属性枚举顺序。

ES6 [[OwnPropertyKeys]]()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
9.1.12 [[OwnPropertyKeys]]()

调用 O 的 [[OwnPropertyKeys]] 内部方法时,采取以下步骤:

1.使 keys 变为一个空的 List

2.对于整数类型的 O 的每个自己的属性键 P,按照数字索引升序,放到属性列表中

a. 将 P 作为 keys 的最后一个元素。

3.对于 String 类型但不是整数索引的 O 的每个自己的属性键 P,按照属性的创建时间升序,放到属性列表中

a. 将 P 作为 keys 的最后一个元素。

4.对于 Symbol 类型的 O 的每个自己的属性键 P,按照属性的创建时间升序,放到属性列表中

a. 将 P 作为 keys 的最后一个元素。

5.返回 keys。

也就是说,Object的属性遍历按照:数字从小到大 -> 字符串创建顺序 -> Symbol 创建顺序 进行遍历。

ES7 [[OwnPropertyKeys]]()

ES8 [[OwnPropertyKeys]]()

举例

1
2
x = {b:20, 3:2, [Symbol('A')]:2, a:100, 2:1}
> {2: 1, 3: 2, b: 20, a: 100, Symbol(A): 2}

Chrome DevTools调用[[OwnPropertyKeys]]()列出对象的所有属性。

Object.keys的枚举顺序

参考文章:5分钟彻底理解Object.keys

1
2
3
x = {b:20, 3:2, [Symbol('A')]:2, a:100, 2:1}
Object.keys(x)
> ["2", "3", "b", "a"]

Object.keysSymbol类型的属性过滤掉了。

ES8 Object.keys(O)

当Object.keys函数使用参数O调用时,会执行以下步骤:

1. 将参数转换成Object类型的对象(ToObject(O))

Let obj be ? ToObject(O).

ToObject(argument)

1
2
3
4
5
6
7
8
9
Undefined: 抛出TypeError
Null: 抛出TypeError

Boolean: 返回一个新的 Boolean 对象
Number: 返回一个新的 Number 对象
String: 返回一个新的 String 对象
Symbol: 返回一个新的 Symbol 对象

Object: 直接返回Object

举例:

1
2
3
4
5
6
7
8
new Number(123)
> Number {123}
__proto__: Number
[[PrimitiveValue]]: 123

// Number对象没有任何可提取的属性,故返回空数组
Object.keys(123)
> []
1
2
3
4
5
6
7
8
9
10
11
12
new String('test')
> String {"test"}
0: "t"
1: "e"
2: "s"
3: "t"
length: 4
__proto__: String
[[PrimitiveValue]]: "test"

Object.keys('test')
> ["0", "1", "2", "3"]
2. 获取属性列表(EnumerableOwnPropertyNames(obj, “key”))

Let nameList be ? EnumerableOwnProperties(obj, “key”).

调用对象的内部方法OwnPropertyKeys获得对象的ownKeys(List类型),然后声明变量properties(List类型),循环ownKeys将每个元素添加到properties列表中,最终将properties返回。

所以对于Object.keys(O)来说,内部方法OwnPropertyKeys决定了属性列表的顺序。

3. 将List类型的属性列表properties转换为Array得到最终的结果

Return CreateArrayFromList(nameList).

CreateArrayFromList

将List类型转换成Array类型:

  1. 先声明一个变量array,值是一个空数组
  2. 循环属性列表,将每个元素添加到array中
  3. 将array返回
该顺序规则适用于其他API
  1. Object.entries
  2. Object.values
  3. for…in循环
  4. Object.getOwnPropertyNames
  5. Reflect.ownKeys

注意:以上API除了Reflect.ownKeys之外,其他API均会将Symbol类型的属性过滤掉。

ES规范

ECMAScript® 2015 Language Specification(ES6)

ECMAScript® 2016 Language Specification(ES7)

ECMAScript® 2017 Language Specification(ES8)